package lumis.portal.group;

import javax.persistence.*;

import lumis.portal.AccessDeniedException;
import lumis.portal.PortalException;
import lumis.portal.acl.PortalPermissions;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.event.persistence.PortalEventEntityListener;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.stability.StableMinor;
import lumis.portal.transaction.ITransaction;
import lumis.portal.transaction.PortalTransactionFactory;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

/**
 * Defines a group's type.
 * Each {@link GroupConfig} is of a specific group type. This group type 
 * contains configurations that are applied for all its groups.
 *
 * @stereotype entity
 * @composed 1 - * GroupConfig
 * 
 * @version $Revision: 13082 $ $Date: 2011-05-27 16:03:12 -0300 (Fri, 27 May 2011) $
 * @since 4.2.1
 */
@StableMinor(version = "6.0", sinceVersion = "4.2")
@Entity
@Table(name="lum_GroupType")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@EntityListeners(value=PortalEventEntityListener.class)
public class GroupType
{
	/**
	 * The identifier of the Lumis Portal standard group type.
	 * @since 4.2.1
	 */
	public static final String GROUP_TYPE_STANDARD_ID = "lumis.portal.group.standardgrouptype";

	@Id
	private String id;
	private String name;
	private String description;
	@Column(name="groupMembershipProvider")
	private String groupMembershipProviderClassName;

	/**
	 * Returns the identifier for this group type.
	 * @return the identifier.
	 * @since 4.2.1
	 */
	public String getId()
	{
		return id;
	}
	
	/**
	 * Sets the identifier for this group type. Should not be called on a
	 * already persisted group type instance. 
	 * @param id the identifier value to set.
	 * @since 4.2.1
	 */
	public void setId(String id)
	{
		this.id = id;
	}
	
	/**
	 * Returns the name of this group type.
	 * @return the name.
	 * @since 4.2.1
	 */
	public String getName()
	{
		return name;
	}
	
	/**
	 * Sets the name for this group type.
	 * @param name the name.
	 * @since 4.2.1
	 */
	public void setName(String name)
	{
		this.name = name;
	}
	
	/**
	 * Returns the description of this group type.
	 * @return the description.
	 * @since 4.2.1
	 */
	public String getDescription()
	{
		return description;
	}
	
	/**
	 * Sets the description for this group type.
	 * @param description the description.
	 * @since 4.2.1
	 */
	public void setDescription(String description)
	{
		this.description = description;
	}
	
	/**
	 * Returns the class name of the {@link IGroupMembershipProvider} to be
	 * used for this group type.
	 * @return the membership provider class name.
	 * @since 4.2.1
	 */
	public String getGroupMembershipProviderClassName()
	{
		return groupMembershipProviderClassName;
	}
	
	/**
	 * Sets the class name of the {@link IGroupMembershipProvider} to be
	 * used for this group type.
	 * @param groupMembershipProviderClassName the membership provider class name.
	 * @since 4.2.1
	 */
	public void setGroupMembershipProviderClassName(String groupMembershipProviderClassName)
	{
		this.groupMembershipProviderClassName = groupMembershipProviderClassName;
	}
	
	/**
	 * Indicates if this is a custom group type. Custom group types
	 * may be edited or removed; built-in group types are unmodifiable.
	 * @return true if it is built-in, false otherwise.
	 * @since 4.2.1
	 */
	public boolean isCustom()
	{
		return !GROUP_TYPE_STANDARD_ID.equals(getId());
	}
	
	/**
	 * Verifies if the current {@link SessionConfig} has permission for
	 * managing portal's groups.
	 * @throws AccessDeniedException if the current SessionConfig does not have
	 * permission for managing portal's groups.
	 * @since 4.2.1
	 */
	private void checkManageGroupPermission() throws PortalException
	{
		// check permission only once in a GroupType in a transaction
		ITransaction transaction = PortalTransactionFactory.getCurrentTransaction();
		final String permissionCheckedAttribute = "lumis.portal.group.GroupType#permissionChecked-" + getId();
		if (transaction.getAttribute(permissionCheckedAttribute) != null)
			return;
		
		// check permission
		if (!ManagerFactory.getPortalAclManager().checkPermission(
				SessionConfig.getCurrentSessionConfig(), 
				PortalPermissions.MANAGE_GROUP, transaction))
		{
			throw new AccessDeniedException();
		}
		
		transaction.setAttribute(permissionCheckedAttribute, Boolean.TRUE);
	}
	
	@PreRemove
	void doPreRemove() throws PortalException
	{
		doPreUpdateOrRemove();
	
		GroupManager.deleteGroupTypeConfiguration(getId());
	}
	
	@PreUpdate
	void doPreUpdateOrRemove() throws PortalException
	{
		// check if modification is allowed
		if (!isCustom())
			throw new IllegalStateException("Only custom group types may be modified or removed");
		checkManageGroupPermission();
		
		// destroy existing membershipProvider, so it will be re-created
		ManagerFactory.getGroupManager().invalidateGroupMembershipProvider(
				SessionConfig.getCurrentSessionConfig(), getId(),
				PortalTransactionFactory.getCurrentTransaction());
	}
	
	@PrePersist
	void doPrePersist() throws PortalException
	{
		checkManageGroupPermission();
	}
}